//
//  YLEmptyDataTableView.swift
//  Swift-Codedase
//
//  Created by xyanl on 2023/9/12.
//

import UIKit

/// UITableView 显示占位符, 在没有结果、没有网络等
/// 作为 UITableView 的基类，都继承 YLEmptyDataTableView
open class YLEmptyDataTableView: UITableView {
    
    // MARK: - 公共属性
    
    /// provider 属性负责占位符视图和数据
    final var provider = YLEmptyDataProvider.default {
        willSet {
            /// 在更改占位符数据之前，我们应该确保 tableview 处于默认配置中。否则，如果 dataSource 和 delegate 在占位符配置中，并且我们设置了新数据，旧的数据将被释放，我们将失去 defaultDataSource 和 defaultDelegate (它们将被设置为 nil)
            clear()
        }
    }
    
    /// 充当视图占位符的委托的对象。
    /// 委托必须采用PlaceholderDelegate协议。不保留委托。
    weak var emptyDataDelegate: YLEmptyDataImplementDelegate? {
        didSet {
            setup()
            registerEmptyDataTableViewCell()
        }    
    }
    
    
    /// 作为UITableViewDataSource的对象。
    /// 数据源必须采用UITableViewDataSource协议。不保留数据源。
    weak open override var dataSource: UITableViewDataSource? {
        didSet {
            // 我们只保存初始数据源(而不是占位符数据源)，以便返回到初始数据
            if  dataSource is YLEmptyDataImplement {
                return
            }
            defaultDataSource = dataSource
        }
    }
    
    /// 充当UITableViewDelegate的对象。
    /// 委托必须采用UITableViewDelegate协议。不保留委托。
    open override weak var delegate: UITableViewDelegate? {
        didSet {
            // 我们只保存初始委托(而不是占位符委托)，以允许返回到初始委托
            if  delegate is YLEmptyDataImplement {
                return
            }
            defaultDelegate = delegate
        }
    }
    
    /// 返回tableHeaderView。
    /// 默认值为空。table header不同于section header。
    open override var tableHeaderView: UIView? {
        didSet {
            if tableHeaderView == nil { return }
            defaultTableHeaderView = tableHeaderView
        }
    }
    
    /// 返回显示在表下方的附件视图。
    /// 默认值为空。table footer不同于section footer。
    open override var tableFooterView: UIView? {
        didSet {
            if tableFooterView == nil { return }
            defaultTableFooterView = tableFooterView
        }
    }
    
    /// 保留分割线样式，而不是用系统默认值重写
    /// 默认值是UITableViewCell.SeparatorStyle.singleLine
    open override var separatorStyle: UITableViewCell.SeparatorStyle {
        didSet {
            defaultSeparatorStyle = separatorStyle
        }
    }
    
    /// A Boolean value that determines whether bouncing always occurs when the placeholder is shown. The default value is false
    /// 用于确定在显示占位符时是否总是发生弹跳。默认: false
    open var placeholdersAlwaysBounceVertical = false
    
    // MARK: - Private properties
    
    /// The defaultDataSource is used to allow to go back to the initial data source of the table view after switching to a placeholder data source
    /// defaultDataSource 用于允许在切换到占位符数据源后返回到表视图的初始数据源
    internal weak var defaultDataSource: UITableViewDataSource?
    
    /// The defaultDelegate is used to allow to go back to the initial delegate of the table view after switching to a placeholder delegate
    /// defaultDelegate用于允许在切换到占位符委托后返回到表视图的初始委托
    internal weak var defaultDelegate: UITableViewDelegate?
    
    /// The defaultSeparatorStyle is used to save the tableview separator style, because, when you switch to a placeholder, is changed to `.none`
    /// defaultSeparatorStyle用于保存tableview分隔符样式，因为当你切换到占位符时，它会被更改为' .none '
    fileprivate var defaultSeparatorStyle: UITableViewCell.SeparatorStyle!
    
    /// The defaultAlwaysBounceVertical is used to save the tableview bouncing setup, because, when you switch to a placeholder, the vertical bounce is disabled
    /// defaultalwaysbounceververtical 用于保存tableview弹跳设置，因为当你切换到占位符时，垂直弹跳被禁用
    fileprivate var defaultAlwaysBounceVertical: Bool!
    
    /// 当你切换到占位符时，defaultTableViewHeader用于保存HeaderView
    fileprivate var defaultTableHeaderView: UIView?
    
    /// 当你切换到占位符时，defaultTableViewFooter用于保存FooterView
    fileprivate var defaultTableFooterView: UIView?
    
    // MARK: - 初始化方法
    public override init(frame: CGRect, style: UITableView.Style) {
        super.init(frame: frame, style: style)
    }
    
    public init(frame: CGRect, style: UITableView.Style, delegate: AnyObject) {
        self.emptyDataDelegate = (delegate as! any YLEmptyDataImplementDelegate)
        super.init(frame: frame, style: style)
        self.dataSource = (delegate as! any UITableViewDataSource)
        self.delegate = (delegate as! any UITableViewDelegate)
        setup()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    /// Config the table view to be able to show placeholders
    /// 配置 YLEmptyDataTableView 以能够显示占位符
    private func setup() {
        register(YLEmptyDataTableViewCell.self, forCellReuseIdentifier: "YLEmptyDataTableViewCell")
        
        defaultSeparatorStyle = separatorStyle
        defaultAlwaysBounceVertical = alwaysBounceVertical
        
        defaultTableHeaderView = tableHeaderView
        defaultTableFooterView = tableFooterView
        
        emptyDataDelegate?.customEmptyDataProvider(self)
    }
    
    // MARK: - 管理 UITableView 和 占位符
    
    /// 切换不同的 dataSource 和 delegate
    /// - Parameters:
    ///   - theDataSource: the selected data source
    ///   - theDelegate: the selected delegate
    internal func switchTo(dataSource theDataSource: UITableViewDataSource?, delegate theDelegate: UITableViewDelegate? = nil) {
        // if the data source and delegate are already set, no need to switch
        // 如果数据源和委托已经设置，则无需切换
        if dataSource === theDataSource && delegate === theDelegate {
            return
        }
        
        if let placeholderDataSource = theDataSource as? YLEmptyDataImplement {
            // placeholder configuration
            super.separatorStyle = .none
            alwaysBounceVertical = placeholdersAlwaysBounceVertical
            
            if placeholderDataSource.object.shouldShowTableViewHeader != true { 
                // style = nil or shouldShowTableViewHeader == false
                tableHeaderView = nil
            }
            if placeholderDataSource.object.shouldShowTableViewFooter != true {
                tableFooterView = nil
            }
        } else {
            // default configuration
            separatorStyle = defaultSeparatorStyle
            alwaysBounceVertical = defaultAlwaysBounceVertical
            tableHeaderView = defaultTableHeaderView
            tableFooterView = defaultTableFooterView
        }
        
        dataSource = theDataSource
        delegate = theDelegate
        super.reloadData()
    }
    
    /// tableView 所有 section 中 row 的数量
    private func numberOfRowsInAllSections() -> Int {
        let numberOfSections = defaultDataSource?.numberOfSections?(in: self) ?? 1
        var rows = 0
        for i in 0 ..< numberOfSections {
            rows += defaultDataSource?.tableView(self, numberOfRowsInSection: i) ?? 0
        }
        return rows
    }
    
    // 重新加载tableView, 如果没有row了, 显示没有结果的占位符
    open override func reloadData() {
        // if the tableview is empty we switch automatically to no data placeholder
        // 如果 tableView 为空，我们会自动切换到无数据占位符
        if numberOfRowsInAllSections() == 0 {
            if let _ = emptyDataDelegate?.showEmptyDataForNoData?(in: self) {
                emptyDataDelegate?.showEmptyDataForNoData!(in: self)
                return
            }
        } else {
            clear()
        }
        // if the data source is in no data placeholder, and the user tries to reload data, we will switch automatically to default
        // 如果数据源没有数据占位符，并且用户尝试重新加载数据，我们将自动切换到默认值
        //        if dataSource is YLEmptyDataImplement {
        //            clear()
        //            return
        //        }
        super.reloadData()
    }
    
    /// Called when the adjusted content insets of the scroll view change.
    /// 当滚动视图的调整内容插页发生变化时调用。
    open override func adjustedContentInsetDidChange() {
        if dataSource is YLEmptyDataImplement {
            // Force table view to recalculate the cell height, because the method tableView:heightForRowAt: is called before adjusting the content of the scroll view
            // 强制tableView重新计算单元格高度， 因为方法tableView:heightForRowAt:在调整滚动视图的内容之前被调用
            guard let indexPaths = indexPathsForVisibleRows else {
                return
            }
            reloadRows(at: indexPaths, with: .automatic)
        }
    }
}

// MARK: 实用程序方法切换到占位符
extension YLEmptyDataTableView {
    
    /// 显示占位符
    /// - Parameter data: 占位符data
    func showEmptyDataView(_ data: YLEmptyDataModel) {
        guard let dataSource = provider.dataSourceAndDelegate(with: data.key) else {
            assertionFailure("您的占位符提供程序配置不正确，没有找到带有键:\(String(describing: data.key))的占位符!")
            return
        }
        self.switchTo(dataSource: dataSource, delegate: dataSource)
    }
    
    /// 清空占位符，显示原有数据
    func clear() {
        self.switchTo(dataSource: defaultDataSource, delegate: defaultDelegate)
    }
}
